<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Regulex：JavaScript Regular Expression Visualizer</title>
<style>
body,html * {
margin:0;
padding:0;
font-family:sans-serif;
}
body {
background: #303030;
}
.code,.code * {
  font-family: "DejaVu Sans Mono",monospace;
}
h1 {
font-size:3em;
color:#40C0FF;
margin:14px 0;
padding:0 16px;
padding-top:40px;
padding-bottom: 20px;
border-bottom:2px dashed grey;
min-height:60px;
}
h1 em{
color:#BBE0E0;
font-size:small;
padding-left:2em;
font-style: normal;
font-weight:normal;
display:inline-block;
}
#inputCt,#errorBox {
margin:8px 16px;
}
#inputCt div.re {
background:#EEE;
font-size:1.2em;
line-height: 1.4em;
color:#333;
border:1px solid black;
padding:4px;
font-weight: bold;
word-break:break-all;
word-wrap:break-word;
}
#inputCt div.re table {
border:none;
padding:0;
margin:0;
width:100%;
}
#inputCt div.re .input {
color:#3030C0;
padding:0 2px;
border:none;
background:#EEE;
font-size:1.2em;
line-height: 1.4em;
height:1.4em;
font-weight: bold;
word-break:break-all;
word-wrap:break-word;
margin:0;
width:auto;
}
#inputCt div.re #input {
display:table;
width:100%;
}
#inputCt label {
  color:white;
  cursor:pointer;
  display:inline-block;
}
#inputCt label input {
  margin-right:4px;
}
#errorBox {
background:#EEE;
font-size:1.2em;
line-height: 1.4em;
border:1px solid black;
padding:4px;
color:darkred;
white-space: pre;
word-wrap:normal;
word-break:keep-all;
display: none;
overflow:auto;
}
#visualIt,#embedIt,#exportIt {
font-size: 16px;
line-height: 16px;
display: inline-block;
margin: 10px;
margin-left:0;
padding:12px;
padding-bottom:10px;
color:#EEF;
border:none;
cursor:pointer;
background: #40B0EF;
background-image: linear-gradient(to bottom, #40B0EF,#3060A0);
border-radius: 12px;
text-decoration: none;
}
#visualIt:hover,#embedIt:hover,#exportIt:hover {
color:#FEF;
background: #3CB0FD;
background-image: linear-gradient(to bottom, #3CB0FD, #3498DF);
text-decoration: none;
}
#graphCt {
padding:4px;
margin:0px 16px;
border:1px solid black;
background: #EEE;
overflow: auto;
cursor:move;
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
#graphCt svg {
display:block;
margin:0 auto;
}
footer {
text-align: center;
border-top:1px solid grey;
margin:1em auto;
padding:10px;
font-size:1.2em;
}
footer,footer a {
color:#EEF;
}
body.embed {
  background: #EEE;
}
body.embed,body.embed #graphCt {
  margin:0;
  padding:0;
  border:none;
}
body.embed h1,
body.embed #inputCt,
body.embed #github {
  display:none !important;
}
body.embed footer {
  display:none;
}
body.embed div.footer,body.embed div.footer a {
  color:white;
  font-size: 14px;
}
body.embed div.footer {
  display:block;
  width:auto;
  margin:0;
  padding:2px 10px;
  background:rgb(75,0,130);
  opacity: 0.9;
  position:fixed;
  right:0;
  bottom:0;
}
body.embed{height:100%;}
body.embed #graphCt {
  height: 100vh;
}
.exportCanvas {
  display:block;
  margin:0 auto;
}
</style>
</head>
<body>
<script>
var params=getParams();
if (params.embed || params.cmd=="export") {
  document.body.className+= " embed";
}

function trim(s) {
  return s.replace(/^\s+/,'').replace(/\s+$/,'');
}

function getParams() {
  var params=location.hash;
  if (!params || params.length<2) {
    params={embed:false,re:"",highlight:true,flags:''};
  } else {
    params=params.slice(2);
    params=params.split("&").reduce(function (p,a) {
      a=a.split("=");
      p[a[0]]=a[1];
      return p;
    },{});
    params.embed=params.embed==='true';
    params.flags=params.flags || '';
    params.re=params.re?trim(decodeURIComponent(params.re)):'';
  }
  return params;
}
</script>
<h1>Regulex <em>JavaScript Regular Expression Visualizer.</em></h1>
<div id="inputCt">
    <div class="re code">
      <table>
        <tr>
          <td style="width:1em">/</td>
          <td style="width:auto">
            <input id="input" class="input" value="^(a|b)*?$" />
          </td>
          <td style="width:1em">/</td>
          <td style="width:3em" id="flagBox"></td>
        </tr>
      </table>
    </div>
    <button id="visualIt">Visualize</button>
    <button id="exportIt">Export Image</button>
    <button id="embedIt">Embed On My Site!</button>
    <label><input type="checkbox" name="flag" value="i" />IgnoreCase</label>
    <label><input type="checkbox" name="flag" value="m" />Multiline</label>
    <label><input type="checkbox" name="flag" value="g" />GlobalMatch</label>
</div>
<p id="errorBox" class="code">Error Message</p>
<div id="graphCt" class="code"></div>

<!--script src="http://cdn.staticfile.org/require.js/2.1.15/require.min.js"></script-->
<!--script src="http://libs.useso.com/js/require.js/2.1.11/require.min.js"></script-->

<script type="text/javascript" charset="utf-8">
/*
//var raphael="http://cdn.staticfile.org/raphael/2.1.2/raphael-min.js";
var raphael="http://libs.useso.com/js/raphael/2.1.2/raphael-min.js";
*/
function $(id) {return document.getElementById(id)}
function $$(q) {return document.querySelector(q)}

var raphael='src/libs/raphael';
var visualize='src/visualize';
var parse='src/parse';
var Kit='src/Kit';

if (params.debug) {
  document.write('<script src="src/libs/require.js" charset="utf-8"><'+'/script>');
  window.addEventListener('DOMContentLoaded',function () {
    require([raphael,visualize,parse,Kit],init);
  });
} else {
  document.write('<script src="dest/regulex.js" charset="utf-8"><'+'/script>');
  window.addEventListener('DOMContentLoaded',function () {
    raphael=require('regulex').Raphael;
    parse = require('regulex').parse;
    visualize = require('regulex').visualize;
    Kit=require('regulex').Kit;
    init(raphael,visualize,parse,Kit);
  });

}


function init(R,visualize,parse,K) {
var paper = R('graphCt', 10, 10);
var input=$('input');
var inputCt=$('inputCt');
var visualBtn=$('visualIt');
var embedBtn=$('embedIt');
var exportBtn=$('exportIt');
var errorBox=$('errorBox');
var flags=document.getElementsByName('flag');
var flagBox=$('flagBox');
var getInputValue=function () {
    return trim(input.value);
};
var setInputValue=function (v) {
  return input.value=trim(v);
};
if (params.flags) {
  setFlags(params.flags);
}
if (params.re) {
  setInputValue(params.re);
}
visualIt();
if (params.cmd=='export') {
  showExportImage();
  return;
} else {
  initListeners();
  dragGraph($('graphCt'));
}






function visualIt(skipError) {
  var re=getInputValue();
  changeHash();
  hideError();
  var ret;
  try {ret=parse(re)}
  catch (e) {
    if (e instanceof parse.RegexSyntaxError) {
      if (!skipError) {
        showError(re,e);
      }
    } else throw e;
    return false;
  }
  visualize(ret,getFlags(),paper);
  return true;
}

function hideError() {
  errorBox.style.display='none';
}
function showError(re,err) {
  errorBox.style.display='block';
  var msg=["Error:"+err.message,""];
  if (typeof err.lastIndex==='number') {
    msg.push(re);
    msg.push(K.repeats('-',err.lastIndex)+"^");
  }
  setInnerText(errorBox,msg.join("\n"));
}

function serializeHash(params) {
  var re=getInputValue();
  var flags=getFlags();
  return "#!" + 
    (params.debug?"debug=true&":"") + 
    (params.cmd?"cmd="+params.cmd+"&":"") + 
    (params.embed?"embed=true&":"") + 
    "flags="+flags+"&re="+encodeURIComponent(params.re=re);
  
}

function changeHash() {
  location.hash=serializeHash(params);
}

function initListeners() {
  var LF='\n'.charCodeAt(0),CR='\r'.charCodeAt(0);
  input.addEventListener('keydown',onEnter);
  input.addEventListener('keyup',onKeyup);
  input.addEventListener('paste',function (evt) {
    var content=trim(evt.clipboardData.getData('text'));
    if (content[0]==='/' && /\/[img]*$/.test(content)) {
      evt.preventDefault();
      var endIndex=content.lastIndexOf('/');
      setFlags(content.slice(endIndex+1));
      content=content.slice(1,endIndex);
      setInputValue(content);
    }
    setTimeout(visualIt,50);
  });
  visualBtn.addEventListener('click',function () {
    visualIt();
  });
  embedBtn.addEventListener('click',function () {
    if (!visualIt()) return false;
    var src=location.href;
    var i=src.indexOf('#');
    src=i>0?src.slice(0,i):src;
    changeHash();
    var re=getInputValue();
    var html='<iframe frameborder="0" width="'+Math.ceil(paper.width)+'" height="'+Math.ceil(paper.height)+'" src="'+src+'#!embed=true&flags='+getFlags()+'&re='+encodeURIComponent(re)+'"></iframe>'
    window.prompt("Copy the html code:",html);
  });

  exportBtn.addEventListener('click',function () {
    var newParams = Object.assign({},params);
    newParams.cmd='export';
    var hash = serializeHash(newParams);
    window.open(location.href.split('#!')[0]+hash,"_blank");
  });


  /*
  window.addEventListener('hashchange',function () {
    if (manualHash) return;
    var p=getParams();
    if (p.re && p.re!==params.re) {
      params.re=p.re;
      setInputValue(p.re);
      visualIt();
    }
  });*/
  for (var i=0,l=flags.length;i<l;i++) {
    flags[i].addEventListener('change',onChangeFlags);
  }

  function onChangeFlags(e) {
    setInnerText(flagBox,getFlags());
    visualIt();
    changeHash();
  }


  var onKeyupTid;
  function onKeyup(e) {
    if (e.keyCode===LF || e.keyCode===CR) {
      return true;
    }
    clearTimeout(onKeyupTid);
    onKeyupTid=setTimeout(function () {
      var skipError=true;
      visualIt(skipError);
    },100);
  }
  function onEnter(e) {
    if (e.keyCode===LF || e.keyCode===CR) {
      e.preventDefault();
      e.stopPropagation();
      visualIt();
    }
  }
}

function getFlags() {
  var fg='';
  for (var i=0,l=flags.length;i<l;i++) {
    if (flags[i].checked) fg+=flags[i].value;
  }
  return fg;
}

function setFlags(fg) {
  for (var i=0,l=fg.length;i<l;i++) {
    if (~fg.indexOf(flags[i].value)) flags[i].checked=true;
    else flags[i].checked=false;
  }
  setInnerText(flagBox,fg);
}

function showExportImage() {
  svg = graphCt.getElementsByTagName('svg')[0];
  var w = svg.clientWidth || parseInt(getComputedStyle(svg).width);
  var h = svg.clientHeight || parseInt(getComputedStyle(svg).height);
  var img = new Image;
  img.width=w;
  img.height=h;
  img.setAttribute('src',svgDataURL(svg));

  var canvas = document.createElement( "canvas" );
  var ctx = canvas.getContext( "2d" );
  
  canvas.className="exportCanvas";
  canvas.setAttribute('width',w);
  canvas.setAttribute('height',h);
  img.onload=function () {
    ctx.drawImage( img, 0, 0 );
    graphCt.style.display='none';
    document.body.appendChild(canvas);
  };
}

function svgDataURL(svg) {
  var svgAsXML = (new XMLSerializer).serializeToString(svg);
  return "data:image/svg+xml," + encodeURIComponent(svgAsXML);
}



function dragGraph(g) {
  g.addEventListener('mousedown',startMove);

  function startMove(e) {
    clearSelect();
    var x=e.clientX,y=e.clientY;
    g.addEventListener('mousemove',onMove);

    document.addEventListener('mouseup',unbind,true);
    window.addEventListener('mouseup',unbind,true);
    function unbind(e) {
      g.removeEventListener('mousemove',onMove);
      document.removeEventListener('mouseup',unbind,true);
      window.removeEventListener('mouseup',unbind,true);
    }

    function onMove(e) {
      var dx=x-e.clientX,dy=y-e.clientY;
      if (dx>0 && g.scrollWidth-g.scrollLeft-g.clientWidth<2
          || dx<0 && g.scrollLeft<1) {
        document.documentElement.scrollLeft+=dx;
        document.body.scrollLeft+=dx;
      } else {
        g.scrollLeft+=dx;
      }
      if (dy>0 && g.scrollHeight-g.scrollTop-g.clientHeight<2
          || dy<0 && g.scrollTop<1) {
        document.documentElement.scrollTop+=dy;
        document.body.scrollTop+=dy;
      } else {
        g.scrollTop+=dy;
      }
      x=e.clientX;
      y=e.clientY;
    }
  }
}

function getInnerText(ele) {
  if (!ele) return '';
  var node=ele.firstChild,results=[];
  if (!node) return '';
  do {
    if (node.nodeType===document.TEXT_NODE) results.push(node.nodeValue);
    else results.push(getInnerText(node));
  } while (node=node.nextSibling);
  return results.join('');
}
function setInnerText(ele,s) {
  ele.innerHTML='';
  var t=document.createTextNode('');
  t.nodeValue=s;
  ele.appendChild(t);
  return s;
}

function clearSelect() {
  if (window.getSelection) {
    if (window.getSelection().empty) {  // Chrome
      window.getSelection().empty();
    } else if (window.getSelection().removeAllRanges) {  // Firefox
      window.getSelection().removeAllRanges();
    }
  } else if (document.selection) {  // IE
    document.selection.empty();
  }
}
}
</script>


<footer>Created by <a href="http://jex.im/" target="_blank">Jex</a>.</footer>
<script>
if (params.embed) {
  document.write('<div class="footer"><a href="'+location.href+'&embed=false" target="_blank">Generated by Regulex.</a></div>');
}
</script>
<a id="github" href="https://github.com/JexCheng/regulex" target="_blank" style="display:inline-block;width:149px;height:149px;position: absolute; top: 0; right: 0; border: 0;"><img style="width:149px;height:149px;position: absolute; top: 0; right: 0; border: 0;" src="https://camo.githubusercontent.com/e7bbb0521b397edbd5fe43e7f760759336b5e05f/68747470733a2f2f73332e616d617a6f6e6177732e636f6d2f6769746875622f726962626f6e732f666f726b6d655f72696768745f677265656e5f3030373230302e706e67" alt="Fork me on GitHub" data-canonical-src="https://s3.amazonaws.com/github/ribbons/forkme_right_darkblue_121621.png"></a>
</body>
</html>
